package com.gdhz.xiaohengqin.device.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gdhz.xiaohengqin.device.constants.WieGandConstant;
import com.gdhz.xiaohengqin.device.model.WieGandRequest;
import com.gdhz.xiaohengqin.device.model.WieGandResponse;
import com.gdhz.xiaohengqin.device.model.entity.AccessGate;
import com.gdhz.xiaohengqin.device.model.entity.DoorInfo;
import com.gdhz.xiaohengqin.device.repository.AccessGateRepository;
import com.gdhz.xiaohengqin.device.service.AccessGateService;
import io.netty.channel.ChannelOption;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import reactor.netty.http.client.HttpClient;

import java.time.Duration;
import java.util.List;

/**
 * @author Swallows
 * @date 2025-05-29 17:43
 */
@Service
public class AccessGateServiceImpl implements AccessGateService {

    private final WebClient webClient;
    private final AccessGateRepository repository;

    private final ObjectMapper objectMapper = new ObjectMapper();

    public AccessGateServiceImpl(WebClient.Builder builder, AccessGateRepository repository) {
        this.webClient = builder
                .baseUrl(WieGandConstant.HTTP_SERVER_URL)
                .clientConnector(new ReactorClientHttpConnector(
                        HttpClient.create()
                                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
                                .responseTimeout(Duration.ofSeconds(10))))
                .defaultHeaders(httpHeaders -> {
                    httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
                    httpHeaders.add(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
                })
                .build();
        this.repository = repository;
    }

    @Override
    public Mono<WieGandResponse> sendActuationCommand(WieGandRequest request) {
        return webClient.post()
                .bodyValue(request)
                .retrieve()
                .bodyToMono(WieGandResponse.class);
    }

    @Override
    public Mono<AccessGate> findBySn(Integer sn) {
        return repository.findById(sn);
    }

    @Override
    public Mono<Void> saveAccessGate(AccessGate accessGate) {

        return repository.save(accessGate).then();
    }

    @Override
    public Flux<AccessGate> findAll() {
        return repository.findAll();
    }

    @Override
    public Mono<Void> saveDoors(Integer gateSn, List<DoorInfo> doorInfos) {

        return Mono.fromCallable(() -> objectMapper.writeValueAsString(doorInfos))
                .subscribeOn(Schedulers.boundedElastic())
                .flatMap(jsonStr -> repository.saveDoors(jsonStr, gateSn))
                .onErrorMap(JsonProcessingException.class, e ->
                        new RuntimeException("Failed to serialize door data", e));
    }
}
